BFG -Suit
Difficulty - Medium

This patch  will give the player a "BFG suit" when they pick up the power armor.  Basically, it puts the player inside
the BFG ball and beams emeinate from him to anything they can damage.  The bfg_armor_think  function is largely based on the original
bfg_think.  also, whem the player is inside the ball, their screen will be tinted green.

First, lets put the two main functions, bfg_armor_think and Use_BFGArmor in place.  In g_weapon.c, at the end of the file add:

/*======================================================================
BFG Armor This will be a bitch....
======================================================================*/

//self is bfg ball
void bfg_armor_think (edict_t *self)
{
// This function is just bfg_think with some small changes
// after all, this is supposed to behave like da bfg
	edict_t	*ent;
	edict_t	*ignore;
	vec3_t	point;
	vec3_t	dir;
	vec3_t	start;
	vec3_t	end;
	int		dmg;
	trace_t	tr;

	if(self->bfg_armor_done < level.time)
		{
		self->owner->client->bfg_blend = 0;
		G_FreeEdict(self);
		return;
		}

	dmg = 15;
	VectorCopy (self->owner->s.origin, self->s.origin);  // make it look like player is inside ball

	// Make owner screen a little green (hey, it rhymes!)
	//SV_AddBlend (0, 1, 0, 0.3, self->owner->client->ps.blend);
   self->owner->client->bfg_blend = 1;


	ent = NULL;
	while ((ent = findradius(ent, self->s.origin, 512)) != NULL) // Make sure we get plenty o bastards
	{
		if (ent == self)              // kant kill ourselves
			continue;

		if (ent == self->owner)       // or our master
			continue;

		if (!ent->takedamage)         // or powerups, medkits etc..
			continue;

		if (!(ent->svflags & SVF_MONSTER) && (!ent->client) && (strcmp(ent->classname, "misc_explobox") != 0))   // If it wont blow up or die, ignore it
			continue;

		VectorMA (ent->absmin, 0.5, ent->size, point);

		VectorSubtract (point, self->s.origin, dir);
		VectorNormalize (dir);

		ignore = self;
		VectorCopy (self->s.origin, start);
		VectorMA (start, 2048, dir, end);
		while(1)
		{
			tr = gi.trace (start, NULL, NULL, end, ignore, CONTENTS_SOLID|CONTENTS_MONSTER|CONTENTS_DEADMONSTER);

			if (!tr.ent)
				break;

			// hurt it if we can
			if ((tr.ent->takedamage) && !(tr.ent->flags & FL_IMMUNE_LASER) && (tr.ent != self->owner))
				T_Damage (tr.ent, self, self->owner, dir, tr.endpos, vec3_origin, dmg, 1, DAMAGE_ENERGY, MOD_BFG_LASER);

			// if we hit something that's not a monster or player we're done
			if (!(tr.ent->svflags & SVF_MONSTER) && (!tr.ent->client))
			{
				gi.WriteByte (svc_temp_entity);
				gi.WriteByte (TE_LASER_SPARKS);
				gi.WriteByte (4);
				gi.WritePosition (tr.endpos);
				gi.WriteDir (tr.plane.normal);
				gi.WriteByte (self->s.skinnum);
				gi.multicast (tr.endpos, MULTICAST_PVS);
				break;
			}

			ignore = tr.ent;
			VectorCopy (tr.endpos, start);
		}

		gi.WriteByte (svc_temp_entity);
		gi.WriteByte (TE_BFG_LASER);
		gi.WritePosition (self->s.origin);
		gi.WritePosition (tr.endpos);
		gi.multicast (self->s.origin, MULTICAST_PHS);
	}

	self->nextthink = level.time + FRAMETIME;
}

void Use_BFGArmor (edict_t *self)
{
// this function is fire_bfg simplifed
	edict_t	*bfg;

	if(self->client->pers.inventory[ITEM_INDEX(FindItem("cells"))] < 50)
		return;

	bfg = G_Spawn();
	VectorCopy (self->s.origin, bfg->s.origin);
	bfg->movetype = MOVETYPE_NONE;
	bfg->solid = SOLID_NOT;
	bfg->s.effects |= EF_BFG | EF_ANIM_ALLFAST;
	bfg->s.modelindex = gi.modelindex ("sprites/s_bfg1.sp2");
	bfg->owner = self;
   bfg->bfg_armor_done = level.time + 1;
	bfg->nextthink = level.time + FRAMETIME;
	bfg->think = bfg_armor_think;
	bfg->classname = "bfg armor blast";
	//bfg->s.sound = gi.soundindex ("weapons/bfg__l1a.wav");
	gi.linkentity (bfg);

	self->client->pers.inventory[ITEM_INDEX(FindItem("cells"))] -=50;
}

OK, now that was a lot.  Now, here's what they do: bfg_armor_think starts off by placing the ball at the player origin. It then blends the
player's screen a little for effect.  Then, it shoots out all the beams.  Use_BFGArmor makes sure you have enough cells (50), then spawns the
ball and subtracts 50 cells.

On to the next step, picking it up.  In g_items.c, around line 19 add:

// BFG suit
void Use_BFGArmor (edict_t *self);

This is just the prototype for the function.  Now on line 724 of g_items.c in Pickup_PowerArmor add:

other->client->pers.inventory[ITEM_INDEX(FindItem("Bfg armor"))]++;
 
That will give it to us when we get the power armor.  Now, to make it an item, on  line 1243 add:

/*=================
BFG armor
=================*/
	{
		"item_bfg_armor",
		Pickup_PowerArmor,
		Use_BFGArmor,
		NULL,
		NULL,
		"misc/ar3_pkup.wav",
		"models/items/armor/shield/tris.md2", EF_ROTATE,
		NULL,
/* icon */		"i_powershield",
/* pickup */	"BFG Armor",
/* width */		60,
		50,
		"Cells",
		IT_WEAPON,
		NULL,
		0,
/* precache */ "misc/power2.wav misc/power1.wav"
	},

There, now it is officially an item.  Now, we need a new variable in the gclient_s structure. so on line 862 of g_local.h insert:

int			bfg_blend;

This is a reference for the screen blending function.  Now we need another in the edict struct, so on line 985add:

float			bfg_armor_done;

Now, about that blend function, in p_view.c at line 422 insert:

// BFG armor
	if(ent->client->bfg_blend)
      SV_AddBlend (0, 1, 0, 0.3, ent->client->ps.blend);

// BFG armor

This will give us the green screen so it looks like we're in the ball.
OK, you're done!  Go try it out, it practically vaporizes any smaller enemy or poorly armored foe(after all, it does do 150pts damage/sec.)
You can select it like any other inventory item to use it.

This Tutorial was written by Tom Nicholson (aka. Anozireth).  I am 15 and live in Seattle, WA.


	   